package hu.arguscab.ai;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @param <E>
 * @param <N> Az objektum, amit a Node tárol
 * @author sharp
 */
public class TreeImpl<E, N> extends AbstractTree<E, N> {

	private Node<N> root;

	private List<Node<N>> nodes = new ArrayList<Node<N>>();
	private Map<Node<N>, List<Node<N>>> childMap = new HashMap<Node<N>, List<Node<N>>>();
	private Map<Node<N>, Node<N>> parentMap = new HashMap<Node<N>, Node<N>>();

	private Map<Node<N>, Map<Node<N>, Edge<E>>> edgeMap = new HashMap<Node<N>, Map<Node<N>, Edge<E>>>();


	/**
	 * @see Graph#setRoot(hu.arguscab.AI.Node) 
	 */
	@Override
	public Node<N> setRoot(N root) {
		Node<N> node = new Node<N>(root) ;
		setRoot(node);
		return node;
	}
	
	@Override
	public void setRoot(Node<N> node) {
		this.root = node;
		node.setDepth(0);
		addNewNode(node);
	}


	/**
	 * Hozzáadja az uj node-ot a gráfhoz.
	 * Hozzáadja a listákhoz a node-nak megfelelő új listákat.
	 * @param node
	 */
	private void addNewNode(Node<N> node) {
		assert containsNode(node) == false;
		
		nodes.add(node);
		childMap.put(node, new ArrayList<Node<N>>());
		edgeMap.put(node, new HashMap<Node<N>, Edge<E>>());
	}

	private void addChild(Node<N> parent, Node<N> child) {
		childMap.get(parent).add(child);
		child.setDepth(parent.getDepth()+1);
	}

	protected void addParent(Node<N> child, Node<N> parent) {
		parentMap.put(child, parent);
	}

	private void addEdge(Node<N> child, Node<N> parent, Edge<E> edge) {
		edgeMap.get(child).put(parent, edge);
		edgeMap.get(parent).put(child, edge);
	}

	/**
	 * @see Graph#addNode(hu.arguscab.AI.Node, hu.arguscab.AI.Node, hu.arguscab.AI.Edge)
	 * A 'from' node-nak már létezik child- és parentList-je
	 */
	@Override
	public void addNode(Node<N> from, Node<N> newNode, Edge<E> edge) {
		assert containsNode(from);

		// A szülőt még ez elött hozzá kell adni, a addedNode miatt, hogy ott már legyen értéke
		// TODO ???
		if ( containsNode(newNode) == false )
			addNewNode(newNode);
		addParent(newNode, from);

		
		addChild(from, newNode);
		addEdge(from, newNode, edge);
	}

	/**
	 * @see Graph#addNodePair(hu.arguscab.AI.NodePair)
	 * @TODO: ha meghivja az addNode-ot, ott megint létrehoz Node-okat --> optimalizálni!
	 */
	@Override
	public void addNodePair( NodePair<E, N> pair ) {
		addNode( pair.getFrom(), pair.getTo(), pair.getEdge() ) ;
	}

	/**
	 * @see Graph#deleteNode(hu.arguscab.AI.Node) 
	 */
	@Override
	public void deleteNode(Node<N> node) {
		throw new UnsupportedOperationException("Not supported.");
	}

	/**
	 * @see Graph#getRoot() 
	 */
	@Override
	public Node<N> getRoot() {
		return root;
	}

	/**
	 * @see Graph#childs(hu.arguscab.AI.Node)
	 */
	@Override
	public Iterable<Node<N>> childs(Node<N> node) {
		return childMap.get(node);
	}


	/**
	 * @see Graph#hasChild(hu.arguscab.AI.Node) 
	 */
	@Override
	public boolean hasChild(Node<N> n) {
		return childMap.get(n) != null && (childMap.get(n).size() > 0);
	}

	/**
	 * @see Graph#getEdge(hu.arguscab.AI.Node, hu.arguscab.AI.Node) 
	 */
	@Override
	public Edge<E> getEdge(Node<N> from, Node<N> to) {
		if ( from == null || to == null )
			return null;
		if ( edgeMap.containsKey(from) == false )
			return null;
		if ( edgeMap.get(from).containsKey(to) == false )
			return null;
		return edgeMap.get(from).get(to);
	}

	/**
	 * @see Graph#degree(hu.arguscab.AI.Node) 
	 */
	@Override
	public int degree(Node<N> node) {
		return childMap.get(node).size() + (parentMap.get(node)!= null ? 1 : 0);
	}

	/**
	 * @see Graph#adjacentTo(hu.arguscab.AI.Node) 
	 */
	@Override
	public Iterable<Node<N>> adjacentTo(Node<N> node) {
		// TODO
		return childMap.get(node);
	}

	/**
	 * @see Graph#containsNode(hu.arguscab.AI.Node) 
	 */
	@Override
	public boolean containsNode(Node<N> node) {
		return nodes.contains(node);
	}

	/**
	 * @see Graph#containsEdge(hu.arguscab.AI.Node, hu.arguscab.AI.Node) 
	 */
	@Override
	public boolean containsEdge(Node<N> from, Node<N> to) {
		if ( from == null || to == null )
			return false;
		return edgeMap.get(from).containsKey(to);
	}

	/**
	 * @see Graph#depthSearchIterator() 
	 */
	@Override
	public Iterable<Node<N>> depthSearchIterator() {
		// TODO: Implementing treespecific iterator
		return new DepthFirstSearchIterator<E, N>(this);
	}

	/**
	 * @see Graph#breathSearchIterator()
	 */
	@Override
	public Iterable<Node<N>> breathSearchIterator() {
		// TODO: Implementing treespecific iterator
		return new BreadthSearchIterator<E, N>(this, getRoot());
	}

	@Override
	public Iterable<Node<N>> leafFirstIterator() {
		return new LeafFirstIterator<E, N>(this);
	}
	
	@Override
	public void clear() {
		parentMap.clear();
		childMap.clear();
		nodes.clear();
		edgeMap.clear();
		root = null;
	}

	@Override
	public Node<N> getParent(Node<N> node) {
		return parentMap.get(node);
	}

	@Override
	public Iterable<Node<N>> breathSearchIterator(Node<N> root) {
		// TODO Auto-generated method stub
		return null;
	}
}
